package utils

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

type AiLIOss struct {
	Endpoint        string   // 地址
	AccessKeyID     string   // accessKeyID
	AccessKeySecret string   // 密钥
	BucketName      string   // 上传的根目录
	BucketUrl       string   // 访问的url地址
	AllowExtList    []string // 允许的上传文件后缀名
	IsRename        bool     // 是否重命名
}

type RegisterOss interface {
	// UploadFile 上传文件
	UploadFile(basePath string, isFolder bool, fileSize int64, file *multipart.FileHeader) (string, error)
	// UploadLocalFile 上传本地文件到阿里云(用于本地先生成的文件然后上传)
	UploadLocalFile(basePath string, isFolder bool, localFile string) (string, error)
}

func NewRegisterOss(endpoint, accessKeyId, accessKeySecret, bucketName, bucketUrl string, isRename bool, allowExtList []string) RegisterOss {
	return &AiLIOss{
		Endpoint:        endpoint,
		AccessKeyID:     accessKeyId,
		AccessKeySecret: accessKeySecret,
		BucketName:      bucketName,
		BucketUrl:       bucketUrl,
		AllowExtList:    allowExtList,
		IsRename:        isRename,
	}
}

func (o *AiLIOss) UploadFile(basePath string, isFolder bool, fileSize int64, file *multipart.FileHeader) (string, error) {
	//指定存储空间
	bucket, err := ClientOSS(o)
	if err != nil {
		fmt.Println("存储空间错误")
		os.Exit(-1)
	}
	if file.Size > fileSize*1024*1024*5 {
		return "", errors.New(fmt.Sprintf("文件大小不能超过:%dM", fileSize))
	}
	// 获取上传文件后缀名
	extString := path.Ext(file.Filename)
	if len(o.AllowExtList) > 0 {
		// 判断文件后缀名
		if err := CheckFileExt(extString, o.AllowExtList); err != nil {
			return "", err
		}
	}
	//打开文件
	fileHandle, err := file.Open()
	if err != nil {
		return "", errors.New(err.Error())
	}
	defer func(fileHandle multipart.File) {
		err := fileHandle.Close()
		if err != nil {
			fmt.Println("关闭失败")
		}
	}(fileHandle)

	fileByte, _ := io.ReadAll(fileHandle)
	//上传到oss上
	filePath := GeneratePath(basePath, file.Filename, isFolder)
	if o.IsRename {
		filePath = GenerateFilePath(basePath, extString, isFolder)
	}
	if IsWindows() {
		filePath = strings.Replace(filePath, "\\", "/", -1)
	}
	err = bucket.PutObject(filePath, bytes.NewReader(fileByte))
	if err != nil {
		return "", errors.New(err.Error())
	}
	respPath := fmt.Sprintf("%s/%s", o.BucketUrl, filePath)
	if IsWindows() {
		respPath = strings.Replace(respPath, "\\", "/", -1)
	}
	return respPath, nil
}

// UploadLocalFile 上传本地文件到阿里云
func (o *AiLIOss) UploadLocalFile(basePath string, isFolder bool, localFile string) (string, error) {
	bucket, err := ClientOSS(o)
	if err != nil {
		return "", errors.New(err.Error())
	}
	// 获取上传文件后缀名
	extString := path.Ext(localFile)
	// 判断文件后缀名
	if err := CheckFileExt(extString, o.AllowExtList); err != nil {
		return "", err
	}
	filePath := GenerateFilePath(basePath, extString, isFolder)
	if err := bucket.PutObjectFromFile(filePath, localFile); err != nil {
		fmt.Println("上传失败")
	}
	return fmt.Sprintf("%s/%s", o.BucketUrl, filePath), nil
}

// ClientOSS 连接到阿里云oss的bucket
func ClientOSS(o *AiLIOss) (*oss.Bucket, error) {
	client, err := oss.New(o.Endpoint, o.AccessKeyID, o.AccessKeySecret)
	if err != nil {
		fmt.Println("阿里云上传错误", err)
		return nil, errors.New(err.Error())
	}
	//指定存储空间
	bucket, err := client.Bucket(o.BucketName)
	if err != nil {
		fmt.Println("存储空间错误")
		os.Exit(-1)
	}
	return bucket, nil
}

// CheckFileExt 判断当前文件格式是否可以上传
func CheckFileExt(extString string, allowArrayList []string) error {
	allowExtMap := make(map[string]bool)
	for _, value := range allowArrayList {
		allowExtMap[value] = true
	}
	if _, ok := allowExtMap[extString]; !ok {
		return errors.New(fmt.Sprintf("当前只支持文件格式:%s,你上传的文件格式是:%s", allowArrayList, extString))
	}
	return nil
}

// GenerateFilePath 是否根据当前年月日生成目录
func GenerateFilePath(basePath, extString string, isFolder bool) string {
	// 根据时间鹾生成文件名
	fileNameInt := time.Now().Unix()
	fileNameStr := strconv.FormatInt(fileNameInt, 10)
	fileName := fileNameStr + extString
	if isFolder {
		return filepath.Join(Mkdir(basePath), "/", fileName)
	} else {
		return filepath.Join(basePath, "/", fileName)
	}
}

// GeneratePath 是否根据当前年月日生成目录
func GeneratePath(basePath, name string, isFolder bool) string {
	fileName := name
	if isFolder {
		return filepath.Join(Mkdir(basePath), "/", fileName)
	} else {
		return filepath.Join(basePath, "/", fileName)
	}
}

// Mkdir 定义一个创建文件目录的方法
func Mkdir(basePath string) string {
	//	1.获取当前时间,并且格式化时间
	folderName := time.Now().Format("2006/01/02")
	folderPath := filepath.Join(basePath, folderName)
	//使用mkdirAll会创建多层级目录
	if err := os.MkdirAll(folderPath, os.ModePerm); err != nil {
		return ""
	}
	return folderPath
}
